package com.auto.printer.utils;

import com.auto.printer.common.constant.Consant;
import com.auto.printer.common.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPrintable;
import org.apache.pdfbox.printing.Scaling;
import sun.print.PageableDoc;

import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.print.attribute.standard.*;
import javax.print.event.PrintJobAdapter;
import javax.print.event.PrintJobEvent;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 文件打印处理类
 */
@Slf4j
public class Printer {


    public static void main(String[] args) {

        // 设置要查找的打印机服务的网络IP地址

        String printerIp = "192.168.1.100";


        // 获取所有可用的打印服务

        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);


        // 遍历并打印所有可用的打印服务

        for (PrintService printService : printServices) {
            log.info("找到指定网络IP的打印机服务： " + printService.getName());
            System.out.println("printService.getAttribute(PrinterName.class) = " + printService.getAttribute(PrinterName.class));
            System.out.println("printService.getAttribute(PrinterState.class) = " + printService.getAttribute(PrinterState.class));
            System.out.println("printService.getAttribute(PrinterStateReasons.class) = " + printService.getAttribute(PrinterStateReasons.class));
            System.out.println("printService.getAttribute(QueuedJobCount.class) = " + printService.getAttribute(QueuedJobCount.class));
            System.out.println("printService.getAttribute(PrinterIsAcceptingJobs.class) = " + printService.getAttribute(PrinterIsAcceptingJobs.class));
        }


    }

    /**
     * 文件打印处理类 不设置软队列 直接使用打印机队列
     * 仅仅处理PDF文件 后端会将文件统一转换为PDF文件
     *
     * @param filePath
     * @return
     */
    public static boolean handlerFile(String filePath, String printerName) throws Exception {

        // 获取所有可用的打印服务
        HashPrintRequestAttributeSet requestAttributeSet = new HashPrintRequestAttributeSet();
        DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;

        PrintService[] ps = PrintServiceLookup.lookupPrintServices(flavor, requestAttributeSet);


        // 遍历并打印所有可用的打印服务

        for (PrintService printService : ps) {
            log.info("找到指定网络IP的打印机服务： " + printService.getName());
        }
        if (filePath == null) {
            throw new MyException("打印失败，未找到可用文件，请检查。");
        }
        //处理文件
        File file = new File(filePath);
        PDDocument document = null;
        try {
            document = PDDocument.load(file);
            PrinterJob printerJob = PrinterJob.getPrinterJob();

            if (printerName != null) {

                //获取打印机列表
                PrintService[] printServices = PrinterJob.lookupPrintServices();


                if (printServices == null || printServices.length <= 0) {
                    throw new MyException("打印失败，未找到可用打印机，请检查。");
                }
                PrintService printService = null;
                //匹配指定打印机
                for (int i = 0; i < printServices.length; i++) {
                    if (printServices[i].getName().contains(printerName)) {
                        printService = printServices[i];

                        break;
                    }
                }
                if (printService != null) {
                    printerJob.setPrintService(printService);
                } else {
                    throw new RuntimeException("打印失败，未找到名称为" + printerName + "的打印机，请检查。");
                }

            }
            //设置纸张及缩放
            PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);


            //设置多页打印
            Book book = new Book();
            PageFormat pageFormat = new PageFormat();
            //设置打印方向
            pageFormat.setOrientation(PageFormat.PORTRAIT); //纵向
            pageFormat.setPaper(getPaper());
            book.append(pdfPrintable, pageFormat, document.getNumberOfPages());
            printerJob.setPageable(book);
            printerJob.setCopies(1);//设置打印份数
            //添加打印属性
            HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
            pars.add(Sides.DUPLEX); //设置单双页

            printerJob.print(pars);

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException(e.getMessage());
        } finally {
            if (document != null) {
                try {
                    document.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (file.exists()) {
                file.delete();
            }
        }

    }

    public static PrintService findPrintService(String printerName) {

        // 获取所有可用的打印服务
        HashDocAttributeSet requestAttributeSet = new HashDocAttributeSet();
        DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;

        PrintService[] ps = PrintServiceLookup.lookupPrintServices(flavor, requestAttributeSet);

        // 遍历并打印所有可用的打印服务

        for (PrintService printService : ps) {
//            log.info("找到指定网络IP的打印机服务： " + printService.getName());
        }
        // 获取默认打印机
        PrintService printService = null;


        //获取打印机列表
        PrintService[] printServices = PrinterJob.lookupPrintServices();


        if (printServices == null || printServices.length <= 0) {
            throw new MyException("打印失败，未找到可用打印机，请检查。");
        }
        //匹配指定打印机
        for (int i = 0; i < printServices.length; i++) {
            if (printServices[i].getName().contains(printerName)) {
                printService = printServices[i];
                return printService;
            }
        }
        throw new MyException("打印失败，未找到此打印机，请联系管理员。");
    }

    /**
     * 等待设备打印
     *
     * @param printerName 打印机名
     * @return boolean
     * @throws Exception 异常
     */
    public static void waitDevicePrint(String printerName) throws Exception {
        while (true) {
            PrintService printService = findPrintService(printerName);
            QueuedJobCount queuedJobCount = printService.getAttribute(QueuedJobCount.class);
            log.info("当前打印队列 {}",queuedJobCount.getValue());
            if (queuedJobCount.getValue() == 0) {
                break;
            }
            //延迟一秒再说
            TimeUnit.SECONDS.sleep(1);
        }
    }

    /**
     * 文档处理程序文件
     *
     * @param file        文件
     * @param printerName 打印机名
     * @param sideFlag    翻页模式 0=单页 1=长边翻页 2=短边翻页
     * @param colorFlag   是否彩色
     * @return boolean
     * @throws Exception 异常
     */
    public static boolean handlerFileDoc(File file, String printerName, Integer sideFlag, Integer colorFlag) throws Exception {


        PrintService printService = findPrintService(printerName);
        try (PDDocument document = PDDocument.load(file);) {

            //设置纸张及缩放
            PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);

            //设置多页打印
            Book book = new Book();
            PageFormat pageFormat = new PageFormat();
            //设置打印方向
            pageFormat.setOrientation(PageFormat.PORTRAIT); //纵向
            pageFormat.setPaper(getPaper());
            book.append(pdfPrintable, pageFormat, document.getNumberOfPages());


            PageableDoc pageableDoc = new PageableDoc(book);


            if (printService != null) {

                DocPrintJob printJob = printService.createPrintJob();

                PrintJobAdapter printJobAdapter = new PrintJobAdapter() {


                    @Override
                    public void printDataTransferCompleted(PrintJobEvent event) {
                        log.info("打印数据传输完成");
                    }

                    @Override
                    public void printJobCompleted(PrintJobEvent event) {
                        log.info("打印作业完成");
                    }

                    @Override
                    public void printJobFailed(PrintJobEvent event) {
                        log.info("打印作业失败");
                    }

                    @Override
                    public void printJobCanceled(PrintJobEvent event) {
                        log.info("打印作业取消");
                    }

                    @Override
                    public void printJobNoMoreEvents(PrintJobEvent pje) {
                        log.info("打印作业没有更多事件");
                        if (isPrinterOutOfPaper(printService)) {
                            log.error("打印机缺纸!");
                        }
                    }

                    @Override
                    public void printJobRequiresAttention(PrintJobEvent pje) {
                        log.info(pje.toString());
                    }
                };
                // 创建打印作业监听器
                printJob.addPrintJobListener(printJobAdapter);

                //添加打印属性
                HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
                if (sideFlag.equals(0)) {
                    pars.add(Sides.ONE_SIDED); //设置单双页
                } else if (sideFlag.equals(1)) {
                    pars.add(Sides.DUPLEX); //设置单双页
                } else {
                    pars.add(Sides.TUMBLE); //设置单双页
                }
                //是否彩色打印
                if (colorFlag.equals(Consant.YesOrNoConstant.YES)) {
                    pars.add(Chromaticity.COLOR); //设置单双页
                } else {
                    pars.add(Chromaticity.MONOCHROME); //设置单双页
                }
                printJob.print(pageableDoc, pars);
                return true;
            } else {
                throw new RuntimeException("打印失败，未找到名称为" + printerName + "的打印机，请检查。");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException(e.getMessage());
        } finally {
            if (file.exists()) {
                file.delete();
            }
        }
    }


    /**
     * 是否缺纸
     *
     * @param printService 打印服务
     * @return boolean
     */
    private static boolean isPrinterOutOfPaper(PrintService printService) {
        PrintServiceAttributeSet attributes = printService.getAttributes();
        PrinterStateReasons reasons = (PrinterStateReasons) attributes.get(PrinterStateReasons.class);
        return reasons != null && reasons.containsKey(PrinterStateReason.MEDIA_NEEDED);
    }


    public static Paper getPaper() {
        Paper paper = new Paper();
        // 默认为A4纸张，对应像素宽和高分别为 595, 842
        int width = 595;
        int height = 842;
        // 设置边距，单位是像素，10mm边距，对应 28px
        int marginLeft = 10;
        int marginRight = 0;
        int marginTop = 10;
        int marginBottom = 0;
        paper.setSize(width, height);
        // 下面一行代码，解决了打印内容为空的问题
        paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));
        return paper;
    }

    /**
     * 获取打印机列表
     *
     * @return
     */
    public static List<String> getPrinterList() {
        ArrayList<String> list = new ArrayList<>();
        try {
            //获取打印机列表
            PrintService[] printServices = PrinterJob.lookupPrintServices();
            if (printServices == null || printServices.length <= 0) {
                System.out.print("打印失败，未找到可用打印机，请检查。");
            }
            //匹配指定打印机
            for (int i = 0; i < printServices.length; i++) {
                list.add(printServices[i].getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}
